#include <iostream>
#include <memory>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

// C++17
#include <sstream>
#include <filesystem>
#include <fstream>

const char *Default_FileName = "text.log";
const char *Default_FilePath = "./tmp/";
const char *Absolute_FilePath = "/home/yang/yang/Linux-plus";
const std::uintmax_t Default_size = 100;

class HandleText
{
public:
    HandleText() : _enabled(true)
    {
    }
    virtual void Execute(std::string &info) = 0;
    void ToNext(std::string &info)
    {
        if (_Next_Handle)
        {
            _Next_Handle->Execute(info);
        }
        else
            std::cout << "到达责任链处理结尾,完成责任链处理..." << std::endl;
    }
    void Enable() { _enabled = true; }
    void Disenable() { _enabled = false; }
    void SetNextHandle(std::shared_ptr<HandleText> handle)
    {
        _Next_Handle = handle;
    }
    std::shared_ptr<HandleText> GetNextHanle()
    {
        return _Next_Handle;
    }

protected:
    bool _enabled;
    std::shared_ptr<HandleText> _Next_Handle;
};

class FormatHandleText : public HandleText
{
public:
    void Execute(std::string &info) override
    {
        if (_enabled)
        {
            std::cout << "开始格式化" << std::endl;
            // [时间][pid][info]
            // 常规的写法
            // std::string ret=std::to_string(time(nullptr))+std::to_string(getpid())+info;
            // C++ string流写法
            std::ostringstream ss;
            ss << "[" << time(nullptr) << "] " << "[" << getpid() << "] " << info << '\n';
            info = ss.str();
        }
        ToNext(info);
    }
};
class SaveFileHandleText : public HandleText
{
public:
    SaveFileHandleText() : _filename(Default_FileName), _filepath(Default_FilePath)
    {
    }
    void Execute(std::string &info) override
    {
        if (_enabled)
        {
            if (!std::filesystem::exists(_filepath))
            {
                try
                {
                    std::filesystem::create_directory(_filepath);
                }
                catch (const std::filesystem::filesystem_error &e)
                {
                    std::cerr << "create_directory error" << e.what() << std::endl;
                }
            }
            //  传统C语言写法
            // FILE* fd=fopen((_filepath+_filename).c_str(),"a");
            // int n=fputs(info.c_str(),fd);
            // if(n<0)
            // {
            //     std::cerr<<"fputs error"<<std::endl;
            // }
            // fclose(fd);
            // C++ 文件流写法
            std::ofstream outFile((_filepath + _filename).c_str(), std::ios::app);
            if (info[info.size() - 1] == '\n')
                outFile << info;
            else
                outFile << info << '\n';
            outFile.close();
            std::cout << "格式化完成..." << std::endl;
        }
        ToNext(info);
    }

private:
    std::string _filepath;
    std::string _filename;
};
// 执行这个节点的前提一定是执行了SaveFIle节点  所以目录一定存在
class BackupHandText : public HandleText
{
public:
    BackupHandText(const std::uintmax_t size = Default_size)
        : _filename(Default_FileName), _filepath(Default_FilePath)
    {
        _size = size;
    }
    void Execute(std::string &info) override
    {
        if (_enabled)
        {
            // 自动检测如果文件大小超过100bytes 则自动备份文件
            // "tmp/text.log" -> "tmp/text.log.时间戳"
            // 1. 检测文件在不在
            if (!std::filesystem::exists((_filepath + _filename).c_str()))
            {
                std::cerr << "文件不存在" << std::endl;
                return; // 后面的节点也就不执行了
            }
            // 检测文件大小
            std::uintmax_t size = 0;
            try
            {
                size = std::filesystem::file_size((_filepath + _filename).c_str());
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
            if (size > _size)
            {
                backup();
                std::cout << "backup end..." << std::endl;
            }
        }
        ToNext(info);
    }

private:
    void backup()
    {
        const std::string file_source = _filepath + _filename;
        const std::string file_backup = file_source + "." + std::to_string(time(nullptr));
        // Linux上，对文件名进行重命名是原子操作。
        std::filesystem::rename(file_source, file_backup);
    }
    std::string _filepath;
    std::string _filename;
    std::uintmax_t _size;
};

class CondenseHandText : public HandleText
{
public:
    CondenseHandText()
        : _filename(Default_FileName), _filepath(Default_FilePath)
    {
    }
    void Execute(std::string &info) override
    {
        if (_enabled)
        {
            // 实现打包备份文件，将所有text.log.*的文件打包
            //  因为要执行 exec* 系列命令 所以需要使用子进程
            const std::string file_source = _filepath + _filename;
            const std::string file_backup = _filename + ".[0-9]*";
            // 首先检测有没有备份文件
            // const std::string filename_backup=_filename+".*";
            // if(std::filesystem::exists(filename_backup.c_str()))
            //  {
            pid_t pid = fork();
            if (pid == 0)
            {
                //  创建一个文件夹用于存放打包文件
                const std::string tgzdir = _filepath + "/Tgz";
                if (!std::filesystem::exists(tgzdir))
                {
                    try
                    {
                        std::filesystem::create_directory(tgzdir);
                    }
                    catch (const std::filesystem::filesystem_error &e)
                    {
                        std::cerr << "create_directory error" << e.what() << std::endl;
                    }
                }
                // child;
                //.log.* -> .log.时间戳.tgz
                const std::string file_tgz = _filename + "." + std::to_string(time(nullptr)) + ".tgz";
                // 调用 exec* 系统调用
                /// 这里有个坑就是，压缩文件必须到所对应对文件目录下进行，如果指明路径就是将整个文件目录全部压缩了

                // 改变当前工作路径
                std::filesystem::current_path(tgzdir);
                // 调用指令
                // l型执行最后要以nullptr结尾，注意
                // 因为 展开通配符是shell的功能，所以这里我们需要将tar指令传给shell来执行，
                // 这里shell需要使用
                std::cout << file_tgz << std::endl;
                std::cout << file_backup << std::endl;
                const std::string command = "tar -czf " + file_tgz + " " + file_backup;
                execlp("sh", "sh", "-c", command.c_str(), NULL);
                exit(1); // 执行替换后不会再执行后面的程序，如果执行到这里，那么说明程序替换失败了。
            }
            // parent
            // 等子进程执行完毕，之后再删除备份文件
            int status = 0;
            pid_t rid = waitpid(pid, &status, 0);
            if (rid > 0)
            {
                if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
                {
                    // 子进程执行成功，开始删除备份文件。
                    // 因为有通配符所以还是需要通过shell来执行
                    pid_t pid = fork();
                    if (pid == 0)
                    {
                        std::filesystem::current_path(_filepath);
                        const std::string command = "rm -f " + file_backup;
                        execlp("sh", "sh", "-c", command.c_str(), NULL);
                        exit(2);
                    }
                    //   std::filesystem::remove(Deletefile.c_str());
                    pid_t rid = waitpid(pid, &status, 0);
                    if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
                    {
                        std::cout << "删除备份文件" << std::endl;
                    }
                }
            }
            //  }
            //  else std::cout<<"backup文件不存在,无法进行压缩"<<std::endl;
        }
        ToNext(info);
    }

private:
    std::string _filepath;
    std::string _filename;
};

// 责任链入口类
class HandlerEntry
{
public:
    HandlerEntry()
        : _work(INT32_MAX)
    {
        _format = std::make_shared<FormatHandleText>();
        _savefile = std::make_shared<SaveFileHandleText>();
        _backup = std::make_shared<BackupHandText>();
        _condense = std::make_shared<CondenseHandText>();

        _format->SetNextHandle(_savefile);
        _savefile->SetNextHandle(_backup);
        _backup->SetNextHandle(_condense);
    }
    void EnableHandler(int work)
    {
        std::shared_ptr<HandleText> cur = _format;
        int i = 0;
        while (cur)
        {
            (work >> i) & 1 ? cur->Enable() : cur->Disenable();
            i++;
            cur=cur->GetNextHanle();
        }
    }
    void Run(std::string &text)
    {
        _format->Execute(text);
    }

private:
    std::shared_ptr<HandleText> _format;
    std::shared_ptr<HandleText> _savefile;
    std::shared_ptr<HandleText> _backup;
    std::shared_ptr<HandleText> _condense;
    int _work; // 位图，最多可以有32个节点
};

/// Chain_of_responsiblity END...
